package com.aube.rvi.net;

import com.aube.rvi.util.Constant;
import com.aube.rvi.util.StorageUtil;
import com.aube.rvi.util.Utils;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;

/**
 * A network performing Volley requests over an {@link HurlStack}.
 */
public class BasicNetwork {

    private static int DEFAULT_POOL_SIZE = 4096;

    protected final HurlStack mHttpStack;

    protected final ByteArrayPool mPool;
    
    /**
     * @param httpStack HTTP stack to be used
     */
    public BasicNetwork(HurlStack httpStack) {
        // If a pool isn't passed in, then build a small default pool that will give us a lot of
        // benefit and not use too much memory.
        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
    }

    /**
     * @param httpStack HTTP stack to be used
     * @param pool a buffer pool that improves GC performance in copy operations
     */
    public BasicNetwork(HurlStack httpStack, ByteArrayPool pool) {
        mHttpStack = httpStack;
        mPool = pool;
    }

    private void logParams(Request<?> request) {
    	if(!Constant.DEBUG)
    		return;
		try {
	    	StringBuffer sb = new StringBuffer();
            String url = request.getUrl();
			sb.append(url).append(url.endsWith("?") ? "" : "?");
			Map<String, String> postdata = request.getParams();
			
			if(postdata == null)
				return;
			
//			Utils.Log("URL_METHOD", postdata.get(OpenApi.API_METHOD)== null?"":postdata.get(OpenApi.API_METHOD).toString());;
		    
			for (Map.Entry<String, String> item : postdata.entrySet()) {
				sb.append("").append(item.getKey()).append("=").append(item.getValue()).append("&");
//				Utils.Log("URL_PARAM", item.getKey()+"="+item.getValue());
			}
			Utils.Log("URL", sb.toString());
    	} catch(Exception e) {
    		e.printStackTrace();
    	}
	 }
    
    public NetworkResponse performRequest(Request<?> request) throws AubeError {
        while (true) {
            byte[] responseContents = null;
            try {
            	// Log Params
            	logParams(request);
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();

                headers.put("Accept-Encoding", "gzip");
                headers.put("Connection" , "Keep-Alive");

                HttpResponse httpResponse = mHttpStack.performRequest(request, headers);

                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                Map<String, String> responseHeaders = convertHeaders(httpResponse.getAllHeaders());

                if(request instanceof FileRequest) {
                    responseContents = entityToFile(httpResponse, (FileRequest) request);
                } else {
                    responseContents = entityToBytes(httpResponse);
                }

                /**
                 * 如果出错
                 */
                if(null == responseContents)
                	return null;
                
                if (statusCode != HttpStatus.SC_OK && statusCode != HttpStatus.SC_NO_CONTENT) {
                    throw new IOException();
                }
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
            } catch (Exception e) {
            	e.printStackTrace();
            	return null;
			}
        }
    }

    public static File newPluginFile(String fileName) {
        return new File(StorageUtil.cachePath, fileName);
    }

    /**
     * @param httpResponse
     * @param request
     * @return
     * @throws IOException
     * @throws AubeError
     */
    private byte[] entityToFile(HttpResponse httpResponse, FileRequest request) throws IOException, AubeError {
        String fileName = request.getFileTitle();

        File file = newPluginFile(fileName);
        File tempFile = new File(StorageUtil.cachePath, System.currentTimeMillis()+"");

        return entityToFile(httpResponse, file, tempFile);
    }

    private byte[] entityToFile(HttpResponse httpResponse, File file, File tempFile) throws IOException, AubeError {
        HttpEntity entity = httpResponse.getEntity();
        FileOutputStream out = new FileOutputStream(tempFile);

        FileInputStream fosfrom = null;
        FileOutputStream fosto = null;
        try {
            Header contentEncoding = httpResponse.getFirstHeader("Content-Encoding");
            InputStream in = entity.getContent();
            if (contentEncoding!=null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
                in = new GZIPInputStream(in);
            }
            if (in == null) {
                throw new AubeError("server error");
            }

            byte[] buffer = new byte[1024];

            int count;

            String dir = file.getAbsolutePath();

            while ((count = in.read(buffer)) != -1) {
                out.write(buffer, 0, count);
            }

            //移动文件到缓存
            fosfrom = new FileInputStream(tempFile);
            fosto = new FileOutputStream(file);

            while ((count = fosfrom.read(buffer)) > 0) {
                fosto.write(buffer, 0, count);
            }

            return dir.getBytes();
        } catch(OutOfMemoryError e) {
            e.printStackTrace();
            System.gc();
            return null;
        } finally {
            try {
                // Close the InputStream and release the resources by "consuming the content".
                entity.consumeContent();

                if (out != null) {
                    out.flush();
                    out.close();
                }

                if(fosfrom != null) {
                    fosfrom.close();
                }

                if(fosto != null) {
                    fosto.flush();
                    fosto.close();
                }

                tempFile.delete();
            } catch (Exception e) {
                // This can happen if there was an exception above that left the entity in
                // an invalid state.
                Utils.Log("Error occured when calling consumingContent %s", e.toString());
            }
        }
    }

    /** Reads the contents of HttpEntity into a byte[]. */
    private byte[] entityToBytes(HttpResponse httpResponse) throws IOException, AubeError {
        HttpEntity entity = httpResponse.getEntity();
    	PoolingByteArrayOutputStream bytes = null;
        byte[] buffer = null;
        try {
        	bytes = new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
        	Header contentEncoding = httpResponse.getFirstHeader("Content-Encoding");
			InputStream in = entity.getContent();
			if (contentEncoding!=null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
				in = new GZIPInputStream(in);
			}
            if (in == null) {
                throw new AubeError("server error");
            }
            buffer = mPool.getBuf(1024);
            int count;
            while ((count = in.read(buffer)) != -1) {
                bytes.write(buffer, 0, count);
            }
            return bytes.toByteArray();
        } catch(OutOfMemoryError e) {
        	e.printStackTrace();
        	System.gc();
        	return null;
        } finally {
            try {
                // Close the InputStream and release the resources by "consuming the content".
                entity.consumeContent();
                
                if(mPool != null)
                	mPool.returnBuf(buffer);
                
                if(bytes != null)
                	bytes.close();
            } catch (Exception e) {
                // This can happen if there was an exception above that left the entity in
                // an invalid state.
                Utils.Log("Error occured when calling consumingContent %s", e.toString());
            }
//        	mPool.returnBuf(buffer);
//        	bytes.close();
        }
    }

    /**
     * Converts Headers[] to Map<String, String>.
     */
    private static Map<String, String> convertHeaders(Header[] headers) {
        Map<String, String> result = new HashMap<String, String>();
        for (int i = 0; i < headers.length; i++) {
            result.put(headers[i].getName(), headers[i].getValue());
        }
        return result;
    }
    
}
